package com.woniuxy.charin.service.impl;
import com.woniuxy.charin.common.RedisConst;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniuxy.charin.enumeration.Assert;
import com.woniuxy.charin.enumeration.BusinessCode;
import com.woniuxy.charin.enumeration.UserStatus;
import com.woniuxy.charin.mapper.PermissionMapper;
import com.woniuxy.charin.mapper.UserRoleMapper;
import com.woniuxy.charin.model.dto.UserAddDto;
import com.woniuxy.charin.model.dto.UserInfoDto;
import com.woniuxy.charin.model.entity.Permission;
import com.woniuxy.charin.model.entity.User;
import com.woniuxy.charin.model.entity.UserActionLog;
import com.woniuxy.charin.model.entity.UserRole;
import com.woniuxy.charin.model.vo.LoginRequest;
import com.woniuxy.charin.model.vo.UserInfoVo;
import com.woniuxy.charin.model.vo.UserVo;
import com.woniuxy.charin.runner.CacheRunner;
import com.woniuxy.charin.service.UserActionLogService;
import com.woniuxy.charin.service.UserService;
import com.woniuxy.charin.mapper.UserMapper;
import com.woniuxy.charin.util.JwtUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.time.Duration;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

/**
* @author KOO
* @description 针对表【user】的数据库操作Service实现
* @createDate 2024-12-25 17:24:20
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private CacheRunner cacheRunner;

    @Autowired
    private UserActionLogService userActionLogService;



    /**
     * 用户登录
     * @param param
     * @param request
     * @return
     */
    @Override
    public UserVo login(LoginRequest param, HttpServletRequest request) {
        //验证验证码
        String code = (String) redisTemplate.opsForValue().get(param.getKey());
        Assert.error(code==null, BusinessCode.INVALID_VERIFICATION);
        Assert.error(!code.equalsIgnoreCase(param.getCode()), BusinessCode.VERIFICATION_ERROR);
        redisTemplate.delete(param.getKey());

        //判断用户名是否存在
        LambdaQueryWrapper<User> queryWrapper =
                Wrappers.lambdaQuery(User.class).eq(User::getAccount, param.getAccount());
        User user = baseMapper.selectOne(queryWrapper);
        Assert.error(user==null, BusinessCode.USER_NOT_EXIST);

        //判断密码是否正确
        boolean matches = bCryptPasswordEncoder.matches(param.getPassword(), user.getPassword());
        Assert.error(!matches, BusinessCode.PASSWORD_ERROR);

        //判断是否用户可用
        Assert.error(user.getStatus()!= UserStatus.ACTIVE.getKey(), BusinessCode.USER_FROZEN_ERROR);

        //1.生成Token
        UserVo userVo = createTokenAndUserVo(request, user);

        //保存系统日志
        UserActionLog userActionLog = new UserActionLog();
        userActionLog.setAccount(userVo.getAccount());
        userActionLog.setActionContent("用户登录");
        userActionLog.setIpAddress(jwtUtil.getRequestIP(request));
        userActionLogService.save(userActionLog);
        return userVo;
    }

    @Override
    @SneakyThrows
    public Map<String, String> getCode() {
        //2) 创建缓存图片：指定宽
        int width = 110, height = 45;
        //参数3：图片的类型，使用RGB类型
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //3) 获取画笔对象
        Graphics graphics = image.getGraphics();
        //4) 设置画笔颜色，并且填充矩形区域
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, width, height);
        //创建一个StringBuider对象
        StringBuilder sb = new StringBuilder();
        //5) 从字符数组中随机得到字符
        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        char[] arr = characters.toCharArray();
        Random random = new Random();
        for (int i = 0; i < 4; i++) {
            //随机从0到长度之间去取索引
            int index = random.nextInt(arr.length);
            //得到1个字符
            char c = arr[index];
            sb.append(c);
            //6) 设置字体，大小为22，设置字的颜色随机
            graphics.setFont(new Font(null, Font.BOLD + Font.ITALIC, 22));
            graphics.setColor(getColor());
            //7) 将每个字符画到图片，位置：5+(i*23), 25
            //将字符转成字符串
            graphics.drawString(String.valueOf(c), 5 + (i * 23), 25);
        }
        //8) 画干扰线8条线，线的位置是随机的，x范围在width之中，y的范围在height之中。
        for (int i = 0; i < 10; i++) {
            graphics.setColor(getColor());
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            graphics.drawLine(x1, y1, x2, y2);
        }
        //9) 将缓存的图片输出到响应输出流中
        //参数1：要输出的缓存图片，参数2：格式，参数3：输出到哪个流中
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(image, "jpeg", outputStream);
        //随机生成一个key，前面加上code::
        String key = RedisConst.USER_VALIDATE_CODE + ":" + UUID.randomUUID().toString().replace("-", "");
        //得到验证码，将生成的字符串放到Redis中，键随机生成，10分钟过期
        redisTemplate.opsForValue().set(key, sb.toString(), Duration.ofMinutes(10));
        log.info("生成的验证码为：{}，Redis中的key是：{}", sb, key);
        Base64.Encoder encoder = Base64.getEncoder();
        String base64Img = "data:image/jpeg;base64," + encoder.encodeToString(outputStream.toByteArray());
        //返回的数据包含验证码的key和图片
        Map<String, String> data = new HashMap<>();
        data.put("key", key);  //redis中键
        data.put("base64Img", base64Img);  //图片
        return data;
    }

    //写一个方法随机获取颜色
    private Color getColor() {
        Random random = new Random();
        //红，绿，蓝取值0-255，随机生成 0-255之间数
        int r = random.nextInt(256);
        int g = random.nextInt(256);
        int b = random.nextInt(256);
        return new Color(r, g, b);
    }

    /**
     * 生成Token，并且封装UserVo对象
     */
    private UserVo createTokenAndUserVo(HttpServletRequest request, User user) {
        //1.生成Token
        HashMap<String, Object> data = new HashMap<>();
        data.put("userId", user.getId());
        data.put("userName", user.getAccount());
        //为了安全，判断IP地址是否相同
        data.put("ip", jwtUtil.getRequestIP(request));
        String token = jwtUtil.createToken(data);
        log.info("创造了一个token"+token);
        String key = RedisConst.USER_TOKEN_PREFIX + ":" + user.getId();
        redisTemplate.opsForValue().set(key, token, Duration.ofDays(1));
        //复制属性：源对象，目标对象
        UserVo userVo = BeanUtil.copyProperties(user, UserVo.class);
        userVo.setToken(token);
        return userVo;
    }

    /**
     * 通过ID查询1个用户
     * @param id
     * keyGenerator = "keyGenerator" (自定义的缓存key生成策略)
     */
    @Override
    public UserVo findById(Long id) {
        //调用数据层查询
        User user = baseMapper.selectById(id);
        Assert.error(user == null, BusinessCode.USER_NOT_EXIST);
        Assert.error(user.getStatus() != UserStatus.ACTIVE.getKey(), BusinessCode.USER_FROZEN_ERROR);
        //复制属性
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(user, userVo);
        return userVo;
    }

    /**
     * 根据用户查找相应的权限
     * @param userId
     * @return
     */
    @Override
    public List<Permission> getNaviMenuByUser(Long userId) {
        List<Permission> userPermissions = permissionMapper.selectPermissionByUser(userId);
        List<Permission> permissionMenus = userPermissions.stream()
                .filter(p -> p.getLevel() == 1)
                .collect(Collectors.toList());
        for (Permission permissionMenu : permissionMenus) {
            List<Permission> chilren = userPermissions.stream()
                    .filter(p -> p.getParentId() == permissionMenu.getId())
                    .collect(Collectors.toList());
            permissionMenu.setChildren(chilren);
        }
        return permissionMenus;
    }

    @Override
    public Page<UserInfoVo> queryUser(UserInfoDto userInfoDto) {

        if (userInfoDto.getStatus()==-1){userInfoDto.setStatus(null);}
        Page<UserInfoVo> userInfoVoPage = baseMapper.selectUsersWithRoles(new Page<>(userInfoDto.getPageNum(), userInfoDto.getPageSize()),
                userInfoDto.getAccount(), userInfoDto.getRoleId(), userInfoDto.getStatus());
        //为每一个UserInfoVo设置createdAccount
        userInfoVoPage.getRecords().forEach(userInfoVo -> {
            if (userInfoVo.getCreatedBy()==null){
                userInfoVo.setCreatedAccount("无");
                return;
            }
            UserVo byId = findById(userInfoVo.getCreatedBy());
            if (byId==null){
                userInfoVo.setCreatedAccount("无");
                return;
            }
            userInfoVo.setCreatedAccount(findById(userInfoVo.getCreatedBy()).getAccount());
        });
        return userInfoVoPage;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addUser(UserAddDto userAddDto,Long userId) {
        User user = BeanUtil.copyProperties(userAddDto, User.class);
        //判断用户名是否重复
        User tempUser = baseMapper.selectOne(Wrappers.lambdaQuery(User.class)
                .eq(User::getAccount, user.getAccount()));
        Assert.error( tempUser != null  , BusinessCode.USER_IS_EXIST);
        //判断手机是否重复
        if(StringUtils.isNotBlank(user.getPhone())){
        tempUser = baseMapper.selectOne(Wrappers.lambdaQuery(User.class)
                .eq(User::getPhone, user.getPhone()));
        Assert.error( tempUser != null , BusinessCode.MOBILE_IS_EXIST);}
        user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        user.setCreatedBy(userId);
        baseMapper.insert(user);
        //查找该新增用户的id
        User newUser = baseMapper.selectOne(Wrappers.lambdaQuery(User.class)
                .eq(User::getAccount, user.getAccount()));
        UserRole userRole = new UserRole();
        userRole.setUserId(Math.toIntExact(newUser.getId()));
        userRole.setRoleId(userAddDto.getRoleId());
        //插入用户角色表
        userRoleMapper.insert(userRole);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void updadteUser(UserInfoVo userInfoVo) {
        User oldUser = baseMapper.selectById(userInfoVo.getId());
        User tempUser = baseMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getAccount, userInfoVo.getAccount()));
        Assert.error(tempUser != null && !oldUser.getId().equals(tempUser.getId()), BusinessCode.USER_IS_EXIST);
        if(StringUtils.isNotBlank(userInfoVo.getPhone())) {
            User tempUser1 = baseMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getPhone, userInfoVo.getPhone()));
            Assert.error(tempUser1 != null && !oldUser.getId().equals(tempUser1.getId()), BusinessCode.MOBILE_IS_EXIST);
        }
        User user = BeanUtil.copyProperties(userInfoVo, User.class);
        baseMapper.updateById(user);
        userRoleMapper.delete(Wrappers.lambdaQuery(UserRole.class).eq(UserRole::getUserId, userInfoVo.getId()));
        UserRole userRole = new UserRole();
        userRole.setUserId(Math.toIntExact(userInfoVo.getId()));
        userRole.setRoleId(Math.toIntExact(userInfoVo.getRoleId()));
        userRoleMapper.insert(userRole);
        cacheRunner.init();
    }

    @Override
    public void lock(Integer id) {
        User user = baseMapper.selectById(id);
        if (user.getStatus() != UserStatus.BLOCKED.getKey()) {
            user.setStatus(UserStatus.BLOCKED.getKey());
            baseMapper.updateById(user);
        }
    }

    /**
     * 批量修改用户状态
     * @param deleteIds
     */
    @Override
    public void batchLock(Integer[] deleteIds) {
       baseMapper.batchUpdateStatus(deleteIds, UserStatus.BLOCKED.getKey());
    }

    @Override
    public void batchActivate(Integer[] deleteIds) {
        baseMapper.batchUpdateStatus(deleteIds, UserStatus.ACTIVE.getKey());
    }

    @Override
    public void logout(String authorization, HttpServletRequest request) {
        //通过token得到用户id
        String userId = jwtUtil.getValue(authorization, "userId");
        //删除redis中的token
        redisTemplate.delete(RedisConst.USER_TOKEN_PREFIX + ":" + userId);
        //记录日志
        UserActionLog userActionLog = new UserActionLog();
        userActionLog.setAccount(jwtUtil.getValue(authorization, "userName"));
        userActionLog.setIpAddress(jwtUtil.getRequestIP(request));
        userActionLog.setActionContent("退出登录");
        userActionLogService.save(userActionLog);
    }


}




